/**
 *  TextMash - simple IDE for Clojure
 *  
 *  Copyright (C) 2010 Aleksander Naszko
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package pl.olek.textmash.matching;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.LinkedList;

import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.JTextComponent;

import pl.olek.textmash.workspace.TextEditor;
import pl.olek.textmash.workspace.Workspace;

/**
 * 
 * @author anaszko
 *
 */
public class OccurrencesMatcher implements CaretListener {
	private static OccurrencesMatcher singleton;
	
	LinkedList<Integer> results = new LinkedList<Integer>();
	public static Color color = new Color(240, 216, 168);
	Thread thread;
	Workspace workspace;
	long refreshAt;
	boolean refreshed;
	String searchedWord;
	public boolean dontDraw;
	AlphaComposite al = 
		AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.55f);
	
	public synchronized static OccurrencesMatcher getInstance() {
		if (singleton == null) {
			singleton = new OccurrencesMatcher();
		}
		return singleton;
	}

	public synchronized void paint(Graphics g, JTextComponent component) {
		
		if (dontDraw || !component.isValid()) {
			return;
		}
		
		if (results.size() > 1) {


			Font pfont = component.getFont();
			FontMetrics pmet = g.getFontMetrics(pfont);

			int fontHeight = pmet.getHeight();
			int fontLeading = pmet.getLeading();
//			int fontDescent = pmet.getDescent();
			int sw = pmet.stringWidth(searchedWord);
			((Graphics2D)g).setComposite(al);
			for (Integer from : results) {
				try {
					Rectangle ws = component.modelToView(from);
					g.setColor(color);
					g.fillRect(ws.x, ws.y + fontLeading, sw, fontHeight);
//					g.setColor(Color.BLACK);
//					g.drawString(searchedWord, ws.x, ws.y + 
//							fontHeight - fontDescent);
				} catch (Exception e) {
					break;
				}
			}
		}
	}

	public OccurrencesMatcher() {
		thread = new Thread() {
			Workspace currentWorkspace = null;

			@Override
			public void run() {
				try {
					while (thread == Thread.currentThread()) {

						synchronized (OccurrencesMatcher.this) {

							if (refreshed) {
								OccurrencesMatcher.this.wait();
								continue;
							} else {
								long toWait = 0;
								while ((toWait = (refreshAt - System
										.currentTimeMillis())) > 0) {
									OccurrencesMatcher.this.wait(toWait);
								}
							}

						}

						currentWorkspace = workspace;

						results.clear();
						
						dontDraw = false;
						
						refreshed = true;

						if (currentWorkspace == null
								|| !currentWorkspace.hasQuickLookup()) {

							if (currentWorkspace != null) {
								currentWorkspace.getTextPane().repaint();
							}
							continue;
						}

						int caret = currentWorkspace.getTextPane()
								.getCaretPosition();
						int from = Math.max(0, caret - 32);
						int to = Math.min(currentWorkspace.getTextPane()
								.getDocument().getLength(), caret + 32);
						int pad = caret - from;

						String word = TextEditor.extractWord(currentWorkspace
								.getTextPane().getText(from, to - from), pad);

						if (word.length() > 0) {

							searchedWord = word;

							String text = currentWorkspace.getTextPane()
									.getTextForSearching(false);

							int a = 0;
							while ((a = text.indexOf(word, a)) != -1) {
								String nextWord = TextEditor.extractWord(text,
										a);

								if (nextWord.equals(word)) {
									results.add(a);
								}

								a += word.length();
							}
						}

						workspace.getTextPane().repaint();

					}
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		};
		thread.setDaemon(true);
		thread.start();
	}

	public synchronized void setWorkspace(Workspace workspace) {
		if (workspace != this.workspace) {
			if (this.workspace != null) {
				this.workspace.getTextPane().removeCaretListener(this);
			}
			if (workspace != null) {
				workspace.getTextPane().addCaretListener(this);
			}
			this.workspace = workspace;
			refresh();
		}
	}

	public synchronized void refresh() {
		refreshAt = System.currentTimeMillis() + 400;
		refreshed = false;
		notify();
	}

	@Override
	public synchronized void caretUpdate(CaretEvent e) {
		refresh();
	}
}
